// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CaeTcpClientErrorTimer.h"
#include "../cae_agent/CaeAgent.h"
#include "../cae_common/CaeMsgCode.h"
#include "CaeClientNotifyTool.h"
#include "CaeParamStorage.h"

namespace {
    constexpr int MAX_DISCONNECT_TIMES = 3600;
    constexpr int DISCONNECT_TIMES = 2;
}

CaeTcpClientErrorTimer::CaeTcpClientErrorTimer(uint32_t interval) : CaeTimer(interval)
{
    LOGD("Construct: CaeTcpClientErrorTimer");
    this->m_disconnectTimes = 0;
    this->m_disconnectTimesMax = CaeConfigManage::GetDisconnectTimes();
    this->m_start = false;
    pthread_mutex_init(&m_disconnectTimesLock, nullptr);
}

CaeTcpClientErrorTimer::~CaeTcpClientErrorTimer()
{
    pthread_mutex_destroy(&m_disconnectTimesLock);
}

void CaeTcpClientErrorTimer::TimeoutCallback()
{
    if (!m_start) {
        return;
    }
    std::string durationString = CaeParamStorage::GetInstance().GetMaxDisconnectDuration();
    auto maxDuration = static_cast<uint32_t>(atoi(durationString.c_str()));
    if (maxDuration > 0 && maxDuration <= MAX_DISCONNECT_TIMES) {
        this->m_disconnectTimesMax = maxDuration;
    } else {
        this->m_disconnectTimesMax = CaeConfigManage::GetDisconnectTimes();
    }

    if (m_disconnectTimes == DISCONNECT_TIMES) {
        LOGI("Heartbeat timeout %d seconds, we think client disconnect, so exec CloseOldClient and wait reconnect.",
            m_disconnectTimes);
        CaeAgent::GetInstance().SetClientDisConnect();
    } else if (m_disconnectTimes >= m_disconnectTimesMax) {
        LOGI("Heartbeat timeout %d seconds, client did not reconnect, so exec CloseClient.", m_disconnectTimes);
        CaeAgent::GetInstance().CloseClient();
        m_start = false;
    } else {
        LOGI("HeartbeatTimer, heartbeat timeout %d seconds.", m_disconnectTimes);
    }
    pthread_mutex_lock(&m_disconnectTimesLock);
    m_disconnectTimes++;
    pthread_mutex_unlock(&m_disconnectTimesLock);
}

uint32_t CaeTcpClientErrorTimer::GetDisconnectTimesMax() const
{
    return m_disconnectTimesMax;
}

void CaeTcpClientErrorTimer::Restart()
{
    if (!m_start) {
        m_start = true;
    }
    pthread_mutex_lock(&m_disconnectTimesLock);
    m_disconnectTimes = 0;
    pthread_mutex_unlock(&m_disconnectTimesLock);
}

void CaeTcpClientErrorTimer::Start()
{
    if (!m_start) {
        m_start = true;
        pthread_mutex_lock(&m_disconnectTimesLock);
        m_disconnectTimes = 0;
        pthread_mutex_unlock(&m_disconnectTimesLock);
    }
}

void CaeTcpClientErrorTimer::Stop() noexcept
{
    m_start = false;
}

CaeTcpClientPauseTimer::CaeTcpClientPauseTimer(uint32_t interval, uint32_t sec) : CaeTcpClientErrorTimer(interval)
{
    uint32_t pauseTime = sec;
    if (pauseTime <= 15 || pauseTime > 3600) {
        pauseTime = CaeConfigManage::GetPauseTimes();
    }
    this->m_pauseTime = pauseTime;
    LOGD("Pause time is %d.", pauseTime);
};

CaeTcpClientPauseTimer::~CaeTcpClientPauseTimer() {}

void CaeTcpClientPauseTimer::TimeoutCallback()
{
    if (!m_start) {
        return;
    }
    LOGD("PauseTimer have paused %d seconds.", m_disconnectTimes);
    if (m_disconnectTimes >= m_pauseTime) {
        LOGI("PauseTimer have paused %d seconds.", m_disconnectTimes);
        CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_HOME_TIMEOUT);
        m_start = false;
    }
    m_disconnectTimes++;
};

CaeNotouchTimer::CaeNotouchTimer(uint32_t interval, const uint32_t timeOut) : CaeTcpClientErrorTimer(interval)
{
    uint32_t timeOutDefault = CaeConfigManage::GetNoTouchTimes();
    this->m_noTouchTime = timeOut < timeOutDefault ? timeOutDefault : timeOut;
    LOGD("CaeTcpClientNotouchTimer is %d seconds.", timeOut);
}

CaeNotouchTimer::~CaeNotouchTimer()
{
    LOGD("Destruct: CaeNotouchTimer");
}

void CaeNotouchTimer::TimeoutCallback()
{
    if (!this->m_start) {
        return;
    }
    if (m_disconnectTimes >= m_noTouchTime) {
        LOGI(" %d seconds without TouchData.", m_disconnectTimes);
        CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_NOTOUCH_TIMEOUT);
        m_start = false;
    }
    m_disconnectTimes++;
};

CaeHandshakeTimeoutTimer::CaeHandshakeTimeoutTimer(CaeWebSocket *ws, uint32_t interval)
    : CaeTcpClientErrorTimer(interval)
{
    LOGD("Construct: CaeHandshakeTimeoutTimer.");
    this->m_handshakeTime = CaeConfigManage::GetHandShakeTimes();
    this->m_webSocket = ws;
};

CaeHandshakeTimeoutTimer::~CaeHandshakeTimeoutTimer()
{
    LOGD("Destruct: CaeHandshakeTimeoutTimer.");
    this->m_webSocket = nullptr;
}

void CaeHandshakeTimeoutTimer::TimeoutCallback()
{
    if (!m_start) {
        return;
    }
    LOGI(" %d seconds without Handshake.", m_disconnectTimes);
    if (m_disconnectTimes >= m_handshakeTime) {
        LOGI(" %d seconds without Handshake.", m_disconnectTimes);
        if (m_webSocket != nullptr) {
            m_webSocket->CloseSocket();
        }
        m_start = false;
    }
    m_disconnectTimes++;
}

CaeTrialTimeoutTimer::CaeTrialTimeoutTimer(const uint32_t maxTrialTime) : CaeTcpClientErrorTimer(maxTrialTime * 1000)
{
    LOGI("Trial time is %d second.", maxTrialTime);
}

CaeTrialTimeoutTimer::~CaeTrialTimeoutTimer() = default;

void CaeTrialTimeoutTimer::TimeoutCallback()
{
    LOGI("Trial timeout.");
    if (!m_start) {
        return;
    }
    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_TRAIL_PLAY_TIMEOUT);
    m_start = false;
}
